import sys
import logging
import string

from itertools import chain
from formencode import Invalid

from clutch.globals import C,G
from clutch.view import engines
from clutch.exceptions import TemplateNotFound

from model import *

log = logging.getLogger(__name__)

class TopeTemplate(object):
    def get(self, engine):
        raise NotImplementedError, 'get'

class TemplateFile(object):
    def __init__(self, fn):
        self.fn = fn
    def get(self, engine):
        loader = engine.loader(self.__class__.__module__, 'templates')
        template = loader.template(self.fn)
        return template

class TemplateText(object):
    def __init__(self, text):
        self.text = text
    def get(self, engine):
        return engine.template_from_string(self.text)

class TopeView(object):
    name = None
    view_type = 'edit'
    template = None
    engine = None
    attrs = {}

    _js_requires = []
    _css_requires = []
    _dojo_requires = []
    _dojo_onload = []

    def __init__(self, model, **kw):
        self.model = model
        cls = self.__class__
        self.engine = engines.get(cls.engine, cls.template)
        for k,v in kw.iteritems():
            setattr(self, k, v)
        if self.name is None:
            self.name = self.model.name

    def display(self, value=None, **kw):
        try:
            value = self.model.from_python(value)
        except Invalid:
            log.warning('Validation (from python) failed in %s', self.__class__)
        ns = dict(
            view=self,
            tope=self.model,
            name=self.name,
            value=value,
            attrs=self.attrs)
        ns.update(kw)
        return self.template.get(self.engine).generate(ns)

    def render(self, value=None, **kw):
        return str(self.display(value, **kw))

    def js_requires(self):
        return self._js_requires

    def css_requires(self):
        return self._css_requires

    def dojo_requires(self):
        return self._dojo_requires

    def dojo_onload(self, name=None):
        if name is None: name = self.name
        ns = dict(
            tope=self,
            name=name,
            attrs=self.attrs)
        for tpl in self._dojo_onload:
            code = string.Template(tpl)
            yield code.safe_substitute(ns)

    def __repr__(self):
        return '<%s %s>' % (self.__class__.__name__, self.name)

class Field(TopeView):
    engine='genshi'
    template=TemplateFile('basic_field.html')
    inner_template = None
    label=None
    help=None
    show_label=True

    def __init__(self, model, **kw):
        TopeView.__init__(self, model, **kw)
        if self.label is None:
            self.label = self.name.capitalize()

class CompoundView(TopeView):
    engine='genshi'
    template=TemplateFile('default_compound.html')
    fields = []

    def __init__(self, model, fields=None, field_names=None, **kw):
        '''Setup a CompoundView'''
        TopeView.__init__(self, model)
        if fields is None:
            if field_names is None: field_names = model.subtopes.keys()
            fields = [ model.subtopes[fn].default_view() for fn in field_names ]
        field_index = dict((f.name, f) for f in fields)
        for attr_name, value_dict in kw.iteritems():
            if value_dict is None: continue
            for field_name, value in value_dict.iteritems():
                setattr(field_index[field_name], attr_name, value)
        self.fields = fields
        cls = self.__class__

    def display(self, value=None, **kw):
        if value is None: value = {}
        return TopeView.display(self, value, **kw)
    
    def js_requires(self):
        l = list(self._js_requires)
        for f in self.fields:
            l += f.js_requires()
        return l

    def css_requires(self):
        l = list(self._css_requires)
        for f in self.fields:
            l += f.css_requires()
        return l

    def dojo_requires(self):
        l = list(self._dojo_requires)
        for f in self.fields:
            l += f.dojo_requires()
        return l

    def dojo_onload(self, name=None):
        if name is None: name = self.name
        l = list(self._dojo_onload)
        for f in self.fields:
            l += f.dojo_onload(self.name_for(f, name))
        return l

    def name_for(self, field, name=None):
        if name is None: name = self.name
        if name is None:
            return field.name
        else:
            return name + '.' + field.name
        
    def value_for(self, value, field):
        if value is None:return None
        result = value.get(field.name, {})
        return result

    def error_for(self, field, name=None):
        name = self.name_for(field, name)
        errs = getattr(C, 'validation_errors', {})
        return errs.get(name, None)
        
    def __repr__(self):
        fields = '\n'.join(repr(f) for f in self.fields)
        fields = '    ' + fields.replace('\n', '\n    ')
        return '<%s %s>\n%s\n</%s>' % (
            self.__class__.__name__, self.name,
            fields,
            self.__class__.__name__)

class GridView(CompoundView):
    engine='genshi'
    template=TemplateFile('gridview.html')
    _dojo_requires = [ 'dojo.data.ItemFileWriteStore', 'dojox.grid.DataGrid' ]
    _css_requires = [ '/static/dojo/dojox/grid/resources/Grid.css',
                      '/static/dojo/dojox/grid/resources/tundraGrid.css',
                      '/static/dojo/dojox/grid/resources/soriaGrid.css',
                      '/static/dojo/dojox/grid/resources/noirGrid.css' ]

    def __init__(self, model, **kw):
        if 'field_names' not in kw:
            kw['field_names'] = [ f.name for f in model.child.fields ]
        CompoundView.__init__(self, model.child, **kw)
        self.model = model

class Repeating(CompoundView):
    engine='genshi'
    template=TemplateFile('repeating.html')
    _dojo_requires = [ 'clutch.topes.Repeating' ]
    _css_requires = [ ]

    def __init__(self, model, **kw):
        if 'field_names' not in kw:
            kw['field_names'] = [ f.name for f in model.child.fields ]
        CompoundView.__init__(self, model.child, **kw)
        self.model = model

        


############################################################
## Form Fields
############################################################

class TextField(Field):
    engine='genshi'
    inner_template=TemplateText('''<input xmlns:py="http://genshi.edgewall.org/"
    name="$name" id="$name" py:attrs="attrs"
    type="text" value="$value"
    dojoType="dijit.form.ValidationTextBox"
    maxLength="$view.max_length"
    promptMessage="$view.help"
    />''')
    min_length=None
    max_length=None
    help=None
    _dojo_requires = ['dijit.form.ValidationTextBox']
    show_help=None # handled by JS

    def display(self, value, **kw):
        return Field.display(self, value, **kw)
    
class RegexField(TextField):
    engine='genshi'
    inner_template=TemplateText('''<input xmlns:py="http://genshi.edgewall.org/"
    name="$name" id="$name" py:attrs="attrs"
    type="text" value="$value"
    dojoType="dijit.form.ValidationTextBox"
    maxLength="$view.max_length"
    regExp="$view.jsRegExp"
    promptMessage="$view.help"
    invalidMessage="$view.invalid_message"
    />''')
    regex=None
    jsRegExp=None
    invalid_message = 'The input is invalid'
    _dojo_requires = ['dijit.form.ValidationTextBox']
    wrapper_args = dict(help=None, invalid_message=None) # Handled by JS
    
    def __init__(self, name, **kw):
        TextField.__init__(self, name, **kw)
        if self.jsRegExp is None:
            self.jsRegExp = self.regex

class SSN(RegexField):
    regex='\d{3}-\d{2}-\d{4}'
    invalid_message = 'Please enter in the format ###-##-####'
    help='Social Security Number (###-##-####)'

class Date(TextField):
    engine='genshi'
    inner_template=TemplateText('''<input xmlns:py="http://genshi.edgewall.org/"
    name="$name" id="$name" py:attrs="attrs"
    type="text" value="$value"
    dojoType="dijit.form.DateTextBox"
    promptMessage="$view.help"
    />''')
    help=None
    _dojo_requires = ['dijit.form.DateTextBox']
    wrapper_args = dict(help=None)
    
class Time(TextField):
    engine='genshi'
    inner_template=TemplateText('''<input xmlns:py="http://genshi.edgewall.org/"
    name="$name" id="$name" py:attrs="attrs"
    type="text" value="$value"
    dojoType="dijit.form.TimeTextBox"
    promptMessage="$view.help"
    />''')
    help=None
    _dojo_requires = ['dijit.form.TimeTextBox']
    wrapper_args = dict(help=None)

class Currency(TextField):
    inner_template=TemplateText('''<input xmlns:py="http://genshi.edgewall.org/"
    name="$name" id="$name" py:attrs="attrs"
    type="text" value="$value"
    dojoType="dijit.form.CurrencyTextBox"
    promptMessage="$view.help"
    />''')
    _dojo_requires = [ 'dijit.form.CurrencyTextBox' ]

class Number(TextField):
    inner_template=TemplateText('''<input xmlns:py="http://genshi.edgewall.org/"
    name="$name" id="$name" py:attrs="attrs"
    type="text" value="$value"
    dojoType="dijit.form.NumberTextBox"
    promptMessage="$view.help"
    />''')
    _dojo_requires = [ 'dijit.form.NumberTextBox' ]
    
class NumberSpinner(Field): pass
class NumberSlider(Field): pass
    
class RadioList(Field):
    inner_template=TemplateFile('radio_list.html')
    _dojo_requires = [ 'dijit.form.CheckBox' ]
    help=None
    show_help=None # handled by JS

class CheckBox(Field):
    inner_template=TemplateText('''<input xmlns:py="http://genshi.edgewall.org/"
    name="$name" id="$name" py:attrs="attrs"
    type="text" value="$value"
    dojoType="dijit.form.CheckBox"
    promptMessage="$view.help"
    />''')
    _dojo_requires = [ 'dijit.form.CheckBox' ]
    help=None
    show_help=None # handled by JS

class LongText(TextField):
    inner_template=TemplateText('''<textarea xmlns:py="http://genshi.edgewall.org/"
    name="$name" id="$name" py:attrs="attrs"
    dojoType="dijit.form.SimpleTextarea"
    rows="$view.rows" cols="$view.cols"
    >$value</textarea>''')
    rows=4
    cols=None
    _dojo_requires = [ 'dijit.form.SimpleTextarea' ]
    
class ResizingLongText(LongText):
    inner_template=TemplateText('''<textarea xmlns:py="http://genshi.edgewall.org/"
    name="$name" id="$name" py:attrs="attrs"
    dojoType="dijit.form.Textarea"
    >$value</textarea>''')
    _dojo_requires = [ 'dijit.form.Textarea' ]

class RichText(LongText):
    inner_template=TemplateFile('rich_text.html')
    _dojo_onload=['''
    var editor = dijit.byId('${name}_editor');
    console.log('Got editor', editor);
    dojo.connect(editor, 'onNormalizedDisplayChanged',
        function() {
            console.log('Display changed');
            dojo.byId('$name').value = dijit.byId('${name}_editor').getValue();
        });    
    ''']
    _dojo_requires = [ 'dijit.Editor' ]

    def display(self, value=None, **kw):
        for part in LongText.display(self, value, **kw):
            print part
            yield part
        
class FilteringSelect(Field):
    engine='genshi'
    inner_template=TemplateFile('filtering_select.html')
    min_length=None
    max_length=None
    help=None
    _dojo_requires = ['dijit.form.FilteringSelect']
    show_help=None # handled by JS

    def display(self, value, **kw):
        return Field.display(self, value, **kw)

############################################################
## Compound Topes
############################################################
class SimpleForm(CompoundView):
    engine='genshi'
    template=TemplateFile('simple_form.html')
    method='post'
    action='.'
    submit_text='Submit'
    _dojo_requires = [ 'dijit.form.Form', 'dijit.form.Button' ]

    def __init__(self, model, fields=None, field_names=None, **kw):
        '''Setup a SimpleForm'''
        cls = self.__class__
        self.method = kw.pop('method', cls.method)
        self.action = kw.pop('action', cls.action)
        self.submit_text = kw.pop('submit_text', cls.submit_text)
        CompoundView.__init__(self, model, fields, field_names, **kw)

    def name_for(self, field, name=None):
        return field.name
        
class FieldSet(CompoundView):
    engine='genshi'
    template_file='fieldset.html'
    show_legend = True
    layout_only = True

class ContentPane(FieldSet):
    template_file='contentpane.html'
    show_legend = True
    layout_only = True
    _dojo_requires=['dijit.layout.ContentPane']

class TabContainer(CompoundView):
    engine='genshi'
    template_file='tabcontainer.html'
    attrs=dict(style='width:100%; height:20em')
    layout_only = True
    _dojo_requires=['dijit.layout.TabContainer']
    
class AccordionContainer(CompoundView):
    engine='genshi'
    template_file='accordioncontainer.html'
    attrs=dict(style='width:100%; height:20em')
    layout_only = True
    _dojo_requires=['dijit.layout.AccordionContainer']
    
